/*
    c++ curl libcurl wrapper
    http://code.google.com/p/curlwork/
    download file demo project
*/

#include "curlwork/info.hpp"
#include "curlwork/operation.hpp"
#include "curlwork/queue_reactor.hpp"

#include <string>
#include <iostream>
#include <iomanip>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <errno.h>


// download two files, by http and ftp
// size about 1M
// http://www.kernel.org/pub/linux/kernel/v1.0/linux-1.0.tar.bz2
// ftp://ftp.kernel.org/pub/linux/kernel/v1.1/linux-1.1.13.tar.gz

class download
{
    typedef curlwork::operation<download> operation_type;
    operation_type webop_;
    FILE *fd_;

public:

    download()
        : webop_(*this)
        , fd_(0)
    {   }

    template<typename T>
    void start(T& reactor,
               const char *file)
    {
        webop_.reset();
        /*
            Remember libcurl takes !!!POINTER!!! to url
            watch out for the lifetime of the object
            you need to hold valid 'file' pointer before operation complete
        */
        webop_.set(curlwork::url(file));

        webop_.set(operation_type::complete(&download::complete));
        webop_.set(operation_type::fail(&download::fail));
        webop_.set(operation_type::write(&download::write));

        webop_.set(curlwork::option(CURLOPT_VERBOSE, 1L));

        if (open(file))
            reactor.add(webop_);
    }

private:

    // write handler
    std::size_t write(const char *buffer,
                      std::size_t size, std::size_t nmemb) throw()
    {
        return fwrite(buffer, size, nmemb, fd_);
    }

    // complete handler
    void complete() throw()
    {
        std::cout << "complete, file size is : "
                  << static_cast<std::size_t>(curlwork::size_download(webop_))
                  << std::endl << std::endl;

        close();
    }

    // fail handler
    void fail(curlwork::error_type error) throw()
    {
        std::cerr << curlwork::effective_url(webop_) << std::endl
                  << " fail : " << curlwork::response_code(webop_)
                  << " - " << curlwork::description(error) << std::endl
                  << std::endl;
    }

    // funcrion return filename
    // it's the sample :D
    const char *crack_url(const char *url)
    {
        const char *p = url + strlen(url);

        while (--p > url)
        {
            if (*p == '/')
                return ++p;
        }

        return url;
    }

    bool open(const char *url)
    {
        const char *file_name = crack_url(url);

        close();

        fd_ = fopen(file_name, "wb");

        if (!fd_)
        {
            std::cerr << "unable to open file : " << file_name
                    << " - " << strerror(errno) << std::endl;

            return false;
        }

        return true;
    }

    void close()
    {
        if (fd_)
        {
            fclose(fd_);
            fd_ = 0;
        }
    }
};

int main()
{
    try
    {
        curlwork::queue_reactor reactor;

        // http download
        download stream1;
        stream1.start(reactor,
            "http://www.kernel.org/pub/linux/kernel/v1.0/linux-1.0.tar.bz2");

        // ftp download
        download stream2;
        stream2.start(reactor,
            "ftp://ftp.kernel.org/pub/linux/kernel/v1.1/linux-1.1.13.tar.gz");

        std::endl(std::cout);

        while (reactor.react())
        {   }
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}
